edit_query.js ➔ rule   F
last analyzed

Complexity

Conditions 23

Size

Total Lines 150
Code Lines 100

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 100
dl 0
loc 150
rs 0
c 0
b 0
f 0
cc 23

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like edit_query.js ➔ rule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
//setting needed variables
2
//Arrays containing groups & rules, indexed by their id
3
var groups = {},
4
    rules = {},
5
    //contains id of the group root group
6
    zero_group_id = "";
7
8
function count(array) {
9
    if (!Array.isArray(array)) {
10
        array = Object.values(array);
11
    }
12
    return array.reduce(function(accumulator, value) {
13
        if (value !== undefined && value !== null) {
14
            accumulator++;
15
        }
16
        return accumulator;
17
    }, 0);
18
}
19
20
function set_postdata() {
21
    if (!$('#preview_persons').data('initialized')) {
22
        $('#preview_persons').data('initialized', true);
23
        return;
24
    }
25
    if ($('#dirmar_rules_editor_container').is(':visible')) {
26
        get_rules_array(zero_group_id);
27
    }
28
    var rules_array = $("#midcom_helper_datamanager2_dummy_field_rules").val(),
29
        grid = $('#preview_persons').jqGrid();
30
31
    grid.setGridParam({'postData': {midcom_helper_datamanager2_dummy_field_rules: rules_array}});
32
}
33
34
function build_select(id, name, cssclass, options, selected, add_empty) {
35
    var select = $('<select class="' + cssclass + '" name="' + name + '" id="' + id + '"></select>'),
36
        option;
37
38
    if (add_empty === true) {
39
        select.append($('<option value=""></option>'));
40
    }
41
    $.each(options, function(key, value) {
42
        if ($.isPlainObject(value)) {
43
            value = value.localized;
44
        }
45
        option = $('<option value="' + key + '">' + value + '</option>');
46
        if (   selected !== false
47
            && selected === key) {
48
            option.prop('selected', true);
49
        }
50
        select.append(option);
51
    });
52
53
    return select;
54
}
55
56
//new object rule
57
function rule(parent, id) {
58
    this.id = parent  + "_rule_" +  id;
59
    this.parent = parent;
60
61
    this.render = function (selected) {
62
        var rule_object = rules[this.id],
63
            rule = $('<div id="' + this.id + '" class="rule"></div>'),
64
            parent_field = $('<input type="hidden" name="' + this.id + '[parent]" value="' + this.parent + '"/>'),
65
            object_select = build_select(this.id + '_object', this.id + '[object]', 'select', org_openpsa_directmarketing_edit_query_property_map, selected, true),
66
            remove_button = $('<i class="button remove_row fa fa-minus"></i>');
67
68
        remove_button.on('click', function(e) {
69
            e.preventDefault();
70
            rule_object.remove();
71
        });
72
73
        object_select
74
            .on('change', function() {
75
                rules[this.parentNode.id].object_select_onchange();
76
            });
77
78
        rule.append(object_select);
79
        rule.append(remove_button);
80
        this.append_add_button(rule);
81
        rule.append(parent_field);
82
83
        $("#" + this.parent + "_add_group").before(rule);
84
    };
85
86
    this.object_select_onchange = function () {
87
        /* Render next inputs based on value */
88
        var selected = $("#" + this.id + "_object").val();
89
        if (!selected) {
90
            $("#" + this.id + "_property").remove();
91
            $("#" + this.id + "_parameter_name").remove();
92
            $("#" + this.id + "_parameter_domain").remove();
93
            $("#" + this.id + "_match").remove();
94
            $("#" + this.id + "_value").remove();
95
            //TODO: Debug
96
            //$.debug("The selected value is empty, abort creating new subinputs");
97
            return;
98
        }
99
100
        if (org_openpsa_directmarketing_edit_query_property_map[selected] === undefined) {
101
            throw selected + ' could not be resolved to property or parameter';
102
        }
103
104
        if (org_openpsa_directmarketing_edit_query_property_map[selected].properties) {
105
             this.render_properties_select(org_openpsa_directmarketing_edit_query_property_map[selected].properties, false);
106
        } else if (org_openpsa_directmarketing_edit_query_property_map[selected].parameters) {
107
            this.render_parameters_div(false, false, false, false);
108
        }
109
    };
110
111
    this.render_properties_select = function (properties, selected) {
112
        var select = build_select(this.id + '_property', this.id + '[property]', 'select', properties, selected, true),
113
            rule = rules[this.id];
114
115
        select.on('change', function() {
116
            rule.render_match_selectinput(false, false);
117
        });
118
119
        $("#" + this.id + "_property").remove();
120
        $("#" + this.id + "_parameter_domain").remove();
121
        $("#" + this.id + "_parameter_name").remove();
122
        $("#" + this.id + "_object").after(select);
123
    };
124
125
    this.render_parameters_div = function (domain, parameter_name, match, value) {
126
        var div_id = this.id + '_parameters',
127
            html = '<div id="' + div_id + '" style="display: inline">',
128
            input_id = this.id + '_parameter_domain',
129
            input_name = this.id + '[parameter_domain]',
130
            input_id2 = this.id + '_parameter_name',
131
            input_name2 = this.id + '[parameter_name]',
132
            value_attr = 'value';
133
134
        if (domain === false) {
135
            value_attr = 'placeholder';
136
            domain = org_openpsa_directmarketing_edit_query_l10n_map.in_domain;
137
            parameter_name = org_openpsa_directmarketing_edit_query_l10n_map.with_name;
138
            match = false;
139
            value = false;
140
        }
141
        $("#" + this.id + "_property").remove();
142
143
144
        html += '<input type="text" class="shorttext" name="' + input_name + '" id="' + input_id + '" ' + value_attr + '="' + domain + '" />';
145
        html += '<input type="text" class="shorttext" name="' + input_name2 + '" id="' + input_id2 + '" ' + value_attr + '="' + parameter_name + '" />';
146
        html += '</div>';
147
        $("#" + this.id + "_object").after(html);
148
149
        this.render_match_selectinput(match, value);
150
    };
151
152
    this.render_match_selectinput = function(match, value) {
153
        if ($("#" + this.id + '_match').length > 0) {
154
            return;
155
        }
156
        var input_id = this.id + "_match",
157
            input_id2 = this.id + '_value',
158
            holder = ($("#" + input_id).val() === 'generic_parameters') ? $("#" + this.id + "_parameter_name") : $("#" + this.id + "_property"),
159
            select = build_select(this.id + '_match', this.id + '[match]', 'select', org_openpsa_directmarketing_edit_query_match_map, match, false),
160
            input = $('<input type="text" class="shorttext" name="' + this.id + '[value]" id="' + input_id2 + '" >');
161
162
        if (value === false) {
163
            value = "";
164
        }
165
        input.val(value);
166
167
        holder.after(input);
168
        holder.after(select);
169
    };
170
171
    this.remove = function() {
172
        var count_child_rules = count(groups[this.parent].child_rules),
173
            count_child_groups = count(groups[this.parent].child_groups);
174
175
        if ($("#" + this.id).children(".add_row").length > 0) {
176
            this.append_add_button($("#" + this.id).prevAll(".rule").first());
177
        }
178
        groups[this.parent].child_rules[this.id] = null ;
179
180
        $("#" + this.id).remove();
181
        delete rules[String(this.id)];
182
        //check if this was last rule
183
        if (count_child_rules === 1) {
184
            if (this.parent !== zero_group_id && count_child_groups === 0) {
185
                //remove parent group from child_groups of the parent group of the parent
186
                delete groups[groups[this.parent].parent].child_groups[this.parent];
187
                groups[this.parent].remove();
188
                delete groups[this.parent];
189
            } else {
190
                groups[this.parent].add_rule(false);
191
            }
192
        }
193
194
        return false;
195
    };
196
197
    this.append_add_button = function(rule) {
198
        $('<i class="button add_row fa fa-plus"></i>')
199
            .on('click', function(e) {
200
                e.preventDefault();
201
                $(this).remove();
202
                groups[rules[rule.attr('id')].parent].add_rule();
203
            })
204
            .appendTo(rule);
205
    };
206
}
207
208
// group-"class"
209
function group(parent, number) {
210
    this.id = parent + "_group_" + number;
211
    this.parent = parent;
212
    this.count_rules = 0;
213
    this.count_groups = 0;
214
215
    this.render = function (selected) {
216
        var group = this,
217
            content_group = $('<div id="' + this.id + '" class="group"></div>'),
218
            group_select = build_select(this.id + '_select', this.id + '[group]', 'groupselect', org_openpsa_directmarketing_group_select_map, selected, false),
219
            group_button = $('<input id="' + this.id + '_add_group" class="add_group" type="button" value="' + org_openpsa_directmarketing_edit_query_l10n_map.add_group + '">'),
220
            parent_field = $('<input type="hidden" name="' + this.id + '[parent]" value="' + this.parent + '"/>'),
221
            group_remove_button;
222
223
        group_button.on('click', function() {
224
            group.add_group(false);
225
        });
226
        if (this.id !== "dirmar_rules_editor_container_group_0") {
227
            group_remove_button = $('<input id=""' + this.id + '_remove_group" class="remove_group" type="button" value="' + org_openpsa_directmarketing_edit_query_l10n_map.remove_group + '">');
228
            group_remove_button.on('click', function() {
229
                group.remove();
230
            })
231
            .appendTo(content_group);
232
        }
233
234
        content_group.append(group_select);
235
        content_group.append($('<br>'));
236
        content_group.append(group_button);
237
        content_group.append(parent_field);
238
239
        if (this.id !== "dirmar_rules_editor_container_group_0") {
240
            $("#" + this.parent + "_add_group").before(content_group);
241
        } else {
242
            $("#" + this.parent).append(content_group);
243
        }
244
    };
245
246
    /**
247
     * adds rule
248
     * @param selected - the selected OBJECT
249
     */
250
    this.add_rule = function add_rule(selected) {
251
        this.count_rules = this.count_rules + 1;
252
        var index = String(this.id + "_rule_" + this.count_rules);
253
        rules[index] = new rule(this.id, this.count_rules);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like rule should be capitalized.
Loading history...
254
        rules[index].render(selected);
255
        if (selected === undefined) {
256
            $("#" + index + "_object")
257
                .val($("#" + index + "_object option:nth-child(2)").attr('value'))
258
                .trigger('change');
259
            $("#" + index + "_property")
260
                .val($("#" + index + "_property option:nth-child(2)").attr('value'))
261
                .trigger('change');
262
        }
263
        this.child_rules[index] = rules[index].id;
264
265
        if ($("#" + index).prev().children(".add_row").length > 0) {
266
            $("#" + index).prev().children(".add_row").remove();
267
        }
268
        return index;
269
    };
270
271
    /**
272
     * adds group
273
     * @param selected - the selected relational operator
274
     */
275
    this.add_group = function(selected) {
276
        this.count_groups = this.count_groups  + 1;
277
        var index = String(this.id + "_group_" + this.count_groups);
278
        groups[index] = new group(this.id, this.count_groups);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like group should be capitalized.
Loading history...
279
        groups[index].render(selected);
280
        this.child_groups[index] = groups[index].id;
281
282
        if (selected === false) {
283
            groups[index].add_rule(false);
284
        }
285
        return index;
286
    };
287
288
    this.remove = function() {
289
        var rule_key,
290
            group_key;
291
        //first remove childs
292
293
        for (rule_key in this.child_rules) {
294
            if (this.child_rules[rule_key] !== null) {
295
                rules[this.child_rules[rule_key]].remove();
296
                delete rules[this.child_rules[rule_key]];
297
            }
298
        }
299
300
        for (group_key in this.child_groups) {
301
            if (   this.child_groups[group_key] !== null
302
                && this.child_groups[group_key] !== undefined) {
303
                if (groups[this.child_groups[group_key]] !== undefined) {
304
                    groups[this.child_groups[group_key]].remove();
305
                }
306
                this.child_groups[group_key] = null;
307
                delete groups[this.child_groups[group_key]];
308
            }
309
        }
310
311
        //check if there is a group in front & no group behind
312
        if (   $("#" + this.id).prev(".group").length > 0
313
            && $("#" + this.id).next(".group").length < 1) {
314
            $("#" + this.id).prev(".group").removeClass("nobottom", "0px");
315
        }
316
317
        $("#" + this.id).remove();
318
        delete groups[String(this.id)];
319
    };
320
321
    this.child_groups = {};
322
    this.child_rules = [];
323
}
324
325
function init(selector, rules) {
326
    var type = rules.type || false;
327
328
    zero_group_id = selector + "_group_0";
329
    groups[zero_group_id] = new group(selector, 0);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like group should be capitalized.
Loading history...
330
    groups[zero_group_id].render(type);
331
332
    // add an empty rule if no rules are currently given
333
    if (   rules.classes === undefined
334
        || rules.classes.length === 0) {
335
        rules.classes = [];
336
        groups[zero_group_id].add_rule(false);
337
    }
338
339
    $('#openpsa_dirmar_edit_query').parent().addClass('disabled');
340
    try {
341
        get_child_rules(zero_group_id, rules.classes);
342
        $('#midcom_helper_datamanager2_dummy_field_rules').hide();
343
    } catch (e) {
344
        $('#dirmar_rules_editor_container').hide();
345
        $('#openpsa_dirmar_edit_query_advanced').parent().addClass('disabled');
346
    }
347
348
    $('#openpsa_dirmar_edit_query_advanced').on('click', function(event) {
349
        event.preventDefault();
350
        get_rules_array(zero_group_id);
351
        $('#midcom_helper_datamanager2_dummy_field_rules').show();
352
        $('#dirmar_rules_editor_container').hide();
353
        $('#openpsa_dirmar_edit_query').parent().removeClass('disabled');
354
        $('#openpsa_dirmar_edit_query_advanced').parent().addClass('disabled');
355
    });
356
    $('#openpsa_dirmar_edit_query').on('click', function(event) {
357
        event.preventDefault();
358
        $('#midcom_helper_datamanager2_dummy_field_rules').hide();
359
        $('#dirmar_rules_editor_container').show();
360
        $('#openpsa_dirmar_edit_query').parent().addClass('disabled');
361
        $('#openpsa_dirmar_edit_query_advanced').parent().removeClass('disabled');
362
    });
363
    $('#show_rule_preview').on('click', function(event) {
364
        event.preventDefault();
365
        $('#preview_persons').jqGrid().trigger('reloadGrid');
366
    });
367
    // adds hover effect to group
368
    $("#org_openpsa_directmarketing_rules_editor")
369
        .on('mouseenter', '#dirmar_rules_editor_container_group_0 .group', function() {
370
            $(this).addClass("focus");
371
        })
372
        .on('mouseleave', '#dirmar_rules_editor_container_group_0 .group', function() {
373
            $(this).removeClass("focus");
374
        });
375
}
376
377
// function to gather the rules from the form & write them into midcom_helper_datamanager2_dummy_field_rules
378
function get_rules_array(parent_id) {
379
    var type = $("#" + parent_id + "_select").val(),
380
        ruleset = {
381
            type: type,
382
            classes: get_rules_groups(parent_id, 0)
0 ignored issues
show
Bug introduced by
The call to get_rules_groups seems to have too many arguments starting with 0.
Loading history...
383
        };
384
    $("#midcom_helper_datamanager2_dummy_field_rules").val(JSON.stringify(ruleset));
385
386
    return true;
387
}
388
389
/**
390
 * function to gather rules for group
391
 *   parent_id - id to check for childs
392
 */
393
function get_rules_groups(parent_id) {
394
    var array_key, ruleset = [],
395
        index, type,
396
        object,
397
        property,
398
        domain, name, match, value;
399
400
    for (array_key in groups[parent_id].child_groups) {
401
        index = groups[parent_id].child_groups[array_key];
402
        type = $("#" + index + "_select").val();
403
404
        if (type !== undefined) {
405
            ruleset.push({
406
                type: type,
407
                groups: type,
408
                classes: get_rules_groups(index)
409
            });
410
        }
411
    }
412
413
    for (array_key in groups[parent_id].child_rules) {
414
        index = groups[parent_id].child_rules[array_key];
415
        object = $("#" + index + "_object").val();
416
        match = $("#" + index + "_match").val();
417
        value = $("#" + index + "_value").val();
418
        if (   match === 'LIKE'
419
            || match === 'NOT LIKE') {
420
            value = "%" + value + "%";
421
        }
422
423
        if (   object !== undefined
424
            && object !== "") {
425
            // parameters must be handled differently
426
            if (object === 'generic_parameters') {
427
                domain  = $("#" + index + "_parameter_domain").val();
428
                name = $("#" + index + "_parameter_name").val();
429
430
                ruleset.push({
431
                    type: 'AND',
432
                    'class': org_openpsa_directmarketing_class_map[object],
433
                    rules: [
434
                        generate_rule('domain', '=', domain),
435
                        generate_rule('name', '=', name),
436
                        generate_rule('value', match, value)
437
                    ]
438
                });
439
            } else {
440
                property  = $("#" + index + "_property").val();
441
442
                //only write if property is chosen
443
                if (   property !== ""
444
                    && match !== "") {
445
                    ruleset.push({
446
                        type: 'AND',
447
                        'class': org_openpsa_directmarketing_class_map[object],
448
                        rules: [
449
                            generate_rule(property, match, value)
450
                        ]
451
                    });
452
                }
453
            }
454
        }
455
    }
456
    return ruleset;
457
}
458
459
function generate_rule(property, match, value) {
460
    return {
461
        property: property,
462
        match: match,
463
        value: value
464
    };
465
}
466
467
/**
468
 * function to set passed rules (passed by get_old_rules->show-campaign-edit_query.php)
469
 * @param parent - parent where the rules should be added
470
 * @param rules_array - array containing the rules
471
 */
472
function get_child_rules(parent, rules_array) {
473
    var map_class,
474
        rule_match, rule_value, rule_domain, rule_parameter_name, rule_property, rule_id,
475
        properties,
476
        parameters, group_id;
477
478
    $.each(rules_array, function (key, value) {
479
        //old-parameter-case
480
        if (value.groups && value.rules) {
481
            //if class is not supported -> error-msg
482
            if (org_openpsa_directmarketing_class_map[value['class']] === undefined) {
483
                throw 'unsupported class';
484
            }
485
            map_class = org_openpsa_directmarketing_class_map[value['class']];
486
            rule_domain = value.groups[0].rules[0].property;
487
            rule_parameter_name = value.groups[0].rules[1].property;
488
            rule_match = value.groups[0].rules[2].match;
489
            rule_value = value.groups[0].rules[2].value;
490
            rule_id = groups[parent].add_rule(map_class);
491
            if (rule_value.substr(0, 1) === '%') {
492
                rule_value = rule_value.substr(1);
493
            }
494
            if (rule_value.substr(rule_value.length - 1, 1) === '%') {
495
                rule_value = rule_value.substr(0, rule_value.length -1);
496
            }
497
            rules[rule_id].render_parameters_div(rule_domain, rule_parameter_name, rule_match, rule_value);
498
        } else if (value.groups) {
499
            //normal group-case
500
            group_id = groups[parent].add_group(value.type);
501
            get_child_rules(group_id, value.classes);
502
        } else if (value.rules) {
503
            //normal rule-case
504
            if (org_openpsa_directmarketing_class_map[value['class']] === undefined) {
505
                throw 'unsupported class';
506
            }
507
508
            map_class = org_openpsa_directmarketing_class_map[value['class']];
509
            rule_match = value.rules[0].match;
510
            rule_value = value.rules[0].value;
511
            rule_property = value.rules[0].property;
512
            rule_id = groups[parent].add_rule(map_class);
513
            properties = false;
514
            parameters = false;
515
            $.each(org_openpsa_directmarketing_edit_query_property_map, function(key, value) {
516
                if (key == map_class) {
517
                    properties = value.properties;
518
                    parameters = value.parameters;
519
                }
520
            });
521
            if (properties) {
522
                // get rid of the % in front & at the end of strings
523
                if (rule_value.substr(0,1) === '%') {
524
                    rule_value = rule_value.substr(1);
525
                }
526
                if (rule_value.substr(rule_value.length - 1, 1) === '%') {
527
                    rule_value = rule_value.substr(0, rule_value.length - 1);
528
                }
529
                rules[rule_id].render_properties_select(properties, rule_property);
530
                rules[rule_id].render_match_selectinput(rule_match, rule_value);
531
            } else if (parameters) {
532
                rule_match = value.rules[2].match;
533
                rule_value = value.rules[2].value;
534
                rule_domain = value.rules[0].value;
535
                rule_parameter_name = value.rules[1].value;
536
                if (rule_value.substr(0,1) === '%') {
537
                    rule_value = rule_value.substr(1);
538
                }
539
                if (rule_value.substr(rule_value.length - 1,1) === '%') {
540
                    rule_value = rule_value.substr(0, rule_value.length - 1);
541
                }
542
                rules[rule_id].render_parameters_div(rule_domain, rule_parameter_name, rule_match, rule_value);
543
            }
544
        }
545
    });
546
}
547